/**
 * 
 */
package com.jbat.gwt.tiledmapcreator.client.common;

import com.google.gwt.core.shared.GWT;
import com.google.gwt.touch.client.Point;

/**
 * 
 */
public class PositionConverter {

  /**
   * Makes determining whether actions should be taken on the row or column of the map more
   * readable.
   */
  public enum RowOrCol {
    ROW, COL;
  }

  private int row; // number of rows
  private int col; // number of columns
  private int unitsPerCell; // canvas units per cell

  private float zoomFactor; // the level of zoom

  public PositionConverter(int row, int col) {
    this(row, col, 100, 100.0f);
  }

  public PositionConverter(int row, int col, int unitsPerCell) {
    this(row, col, unitsPerCell, 100.0f);
  }

  public PositionConverter(int row, int col, int unitsPerCell, float zoomFactor) {
    this.row = row;
    this.col = col;
    this.unitsPerCell = unitsPerCell;
    this.zoomFactor = zoomFactor;
  }

  /**
   * Translates the given map location into the upper left and lower right points that bound the
   * location in the canvas.
   * 
   * @param row - the row of the cell
   * @param col - the column of the cell
   * @return a CanvasLocation with upperLeft and lowerRight points, or null if the map location
   *         doesn't exist
   */
  public CanvasLocation getCanvasArea(int row, int col) {
    if (row > this.row || col > this.col || row < 0 || col < 0) {
      return null;
    }

    // base dimension calculates where (from a grid line) the grid area ends
    int baseDim = unitsPerCell - 1;

    // calculate the left and right X values
    int baseX = col * unitsPerCell;
    int leftX = baseX + 1;
    int rightX = baseX + baseDim;

    // calculate the top and bottom Y values
    int baseY = row * unitsPerCell;
    int topY = baseY + 1;
    int botY = baseY + baseDim;

    // create the points
    Point upperLeft = new Point(leftX, topY);
    Point lowerRight = new Point(rightX, botY);

    // return a new CanvasLocation object
    return new CanvasLocation(upperLeft, lowerRight);
  }

  /**
   * Translates the given canvas coordinates into a Map Location. If a map is viewed as a grid, a
   * MapLocation(0,0) is the top left box and MapLocation(x-1, y-1) is the bottom right
   * 
   * @param unitX
   * @param unitY
   * @return a MapLocation with the proper row and col values, or null if it is not on the map
   */
  public MapLocation getMapLocationAtCoords(int unitX, int unitY) {
    try {
      Integer mapRow = calculateCell(unitY, RowOrCol.ROW); // get the
      // row
      Integer mapCol = calculateCell(unitX, RowOrCol.COL); // get the
      // col

      if (mapRow == null || mapCol == null) {
        return null;
      }

      return new MapLocation(mapRow.intValue(), mapCol.intValue());
    } catch (IllegalArgumentException e) {
      // log the error and return null
      GWT.log(e.getMessage() + e.getStackTrace());
      return null;
    }
  }

  /**
   * @return the unitsPerCell
   */
  public int getUnitsPerCell() {
    return unitsPerCell;
  }

  private Integer calculateCell(int units, RowOrCol rowOrCol) throws IllegalArgumentException {
    // if > than size of row or col, return null
    int testAgainst = 0;

    switch (rowOrCol) {
      case ROW:
        testAgainst = row;
        break;
      case COL:
        testAgainst = col;
        break;
      default:
        // must be one or other
        throw new IllegalArgumentException("The RowOrCol parameter has an invalid value: "
            + "must be either ROW or COL.");
    }

    // if < 0, return null
    if (units < 0) {
      return null;
    }

    if (units > (testAgainst * unitsPerCell)) {
      return null;
    }

    // combined for jit processing
    int r = units % unitsPerCell;
    int q = units / unitsPerCell;

    // if evenly divisible, on a grid line
    if (r == 0) {
      return null;
    }

    // result already truncated
    return q;
  }
}
